package com.geniusgithub.mediarender.player;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.MediaController;
import android.widget.SeekBar;
import android.widget.TextView;

import com.geniusgithub.mediarender.R;

import java.util.Formatter;
import java.util.Locale;


public class PlayerVideoController extends FrameLayout {
  private MediaController.MediaPlayerControl mPlayer;
  private final Context mContext;
  private ViewGroup mAnchor;
  private View mRoot;
  protected SeekBar mProgress;
  protected TextView mEndTime, mCurrentTime,tvTitleView;
  private boolean mShowing;
  private boolean mDragging;
  private int sDefaultTimeout = 5000;
  private static final int FADE_OUT = 1;
  private static final int SHOW_PROGRESS = 2;
  private static final int PROGRESS = 3;
  private final boolean mUseFastForward;
  private boolean mFromXml;
  StringBuilder mFormatBuilder;
  Formatter mFormatter;
  protected ImageView mPauseButton;
//  protected ImageView ivImageBack;
  protected ImageView screenShot;
  protected String mediaName;
  String currentPlaySpeed = "";

  private IBasePlayEngine iBasePlayEngine;

  public IBasePlayEngine getBasePlayEngine() {
    return iBasePlayEngine;
  }

  public void setBasePlayEngine(IBasePlayEngine iBasePlayEngine) {
    this.iBasePlayEngine = iBasePlayEngine;
  }

  public PlayerVideoController(Context context, AttributeSet attrs) {
    super(context, attrs);
    mRoot = this;
    mContext = context;
    mUseFastForward = true;
    mFromXml = true;
  }

  @Override public void onFinishInflate() {
    super.onFinishInflate();
    if (mRoot != null) initControllerView(mRoot);
  }

  public PlayerVideoController(Context context, boolean useFastForward) {
    super(context);
    mContext = context;
    mUseFastForward = useFastForward;
    initFloatingWindow();
  }

  public PlayerVideoController(Context context) {
    this(context, true);
  }

  private void initFloatingWindow() {
    //setOnTouchListener(mTouchListener);
    setFocusable(true);
    setFocusableInTouchMode(true);
    setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    requestFocus();
  }

  // Update the dynamic parts of mDecorLayoutParams
  // Must be called with mAnchor != NULL.
  private void updateFloatingWindowLayout() {
    int[] anchorPos = new int[2];
    mAnchor.getLocationOnScreen(anchorPos);

    // we need to know the size of the controller so we can properly position it
    // within its space
  }

  // This is called whenever mAnchor's layout bound changes
  private final OnLayoutChangeListener mLayoutChangeListener = new OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
                               int oldTop, int oldRight, int oldBottom) {
      updateFloatingWindowLayout();
      if (mShowing) {
        //mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);
      }
    }
  };

  public void setMediaPlayer(MediaController.MediaPlayerControl player) {
    mPlayer = player;
    mHandler.sendEmptyMessage(PROGRESS);
    updatePausePlay();
  }

  /**
   * Set the view that acts as the anchor for the control view.
   * This can for example be a VideoView, or your Activity's main view.
   * When VideoView calls this method, it will use the VideoView's parent
   * as the anchor.
   *
   * @param view The view to which to anchor the controller when it is visible.
   */
  public void setAnchorView(ViewGroup view) {
    if (mAnchor != null) {
      mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener);
    }
    mAnchor = view;
    if (mAnchor != null) {
      mAnchor.addOnLayoutChangeListener(mLayoutChangeListener);
    }
    removeAllViews();
    View v = makeControllerView();
    addView(v, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    mAnchor.removeView(this);
    mAnchor.addView(this, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    v.setVisibility(View.INVISIBLE);
  }

  protected View makeControllerView() {
    mRoot = LayoutInflater.from(mContext).inflate(getControllerLayoutId(), null);
    initControllerView(mRoot);
    return mRoot;
  }

  protected int getControllerLayoutId() {
    return R.layout.media_toolbar_layout;
  }

  protected void initControllerView(View v) {
    mPauseButton = (ImageView) v.findViewById(R.id.btn_play);
    if (mPauseButton != null) {
      mPauseButton.requestFocus();
      mPauseButton.setOnClickListener(mPauseListener);
    }

    mProgress = (SeekBar) v.findViewById(R.id.playback_seeker);
    if (mProgress != null) {
      if (mProgress instanceof SeekBar) {
        SeekBar seeker = (SeekBar) mProgress;
        seeker.setOnSeekBarChangeListener(mSeekListener);
      }
      mProgress.setMax(1000);
    }

    mEndTime = (TextView) v.findViewById(R.id.tv_totalTime);
    mCurrentTime = (TextView) v.findViewById(R.id.tv_curTime);
    tvTitleView = (TextView) v.findViewById(R.id.tv_title);
    tvTitleView.setText(mediaName);
    mFormatBuilder = new StringBuilder();
    mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
  }

  /**
   * Show the controller on screen. It will go away
   * automatically after 3 seconds of inactivity.
   */
  public void show() {
    show(sDefaultTimeout);
  }

  /**
   * Disable pause or seek buttons if the stream cannot be paused or seeked.
   * This requires the control interface to be a MediaPlayerControlExt
   */
  private void disableUnsupportedButtons() {
    try {
      if (mPauseButton != null && !mPlayer.canPause()) {
        mPauseButton.setEnabled(false);
      }
      if (mProgress != null && !mPlayer.canSeekBackward() && !mPlayer.canSeekForward()) {
        mProgress.setEnabled(false);
      }
    } catch (IncompatibleClassChangeError ex) {
      // We were given an old version of the interface, that doesn't have
      // the canPause/canSeekXYZ methods. This is OK, it just means we
      // assume the media can be paused and seeked, and so we don't disable
      // the buttons.
    }
  }

  /**
   * Show the controller on screen. It will go away
   * automatically after 'timeout' milliseconds of inactivity.
   *
   * @param timeout The timeout in milliseconds. Use 0 to show
   * the controller until hide() is called.
   */
  public void show(int timeout) {
    if (!mShowing && mAnchor != null) {
      setProgress();
      if (mPauseButton != null) {
        mPauseButton.requestFocus();
      }
      disableUnsupportedButtons();
      updateFloatingWindowLayout();
      mRoot.setVisibility(View.VISIBLE);
      mShowing = true;
    }
    updatePausePlay();

    // cause the progress bar to be updated even if mShowing
    // was already true.  This happens, for example, if we're
    // paused with the progress bar showing the user hits play.
    mHandler.sendEmptyMessage(SHOW_PROGRESS);

    if (timeout != 0) {
      mHandler.removeMessages(FADE_OUT);
      Message msg = mHandler.obtainMessage(FADE_OUT);
      mHandler.sendMessageDelayed(msg, timeout);
    }
  }

  public boolean isShowing() {
    return mShowing;
  }

  /**
   * Remove the controller from the screen.
   */
  public void hide() {
    if (mAnchor == null) return;

    if (mShowing) {
      try {
        mHandler.removeMessages(SHOW_PROGRESS);
        mRoot.setVisibility(View.GONE);
      } catch (IllegalArgumentException ex) {
        Log.w("MediaController", "already removed");
      }
      mShowing = false;
    }
  }

  private boolean needHide = true;
  private final Handler mHandler = new Handler(new Handler.Callback() {
    @Override public boolean handleMessage(Message msg) {
      int pos;
      switch (msg.what) {
        case FADE_OUT:
          if (needHide) hide();
          break;
        case SHOW_PROGRESS:
          pos = setProgress();
          if (!mDragging && mShowing && mPlayer.isPlaying()) {
            msg = Message.obtain(msg);
            mHandler.sendMessageDelayed(msg, 1000 - (pos % 1000));
          }
          break;
        case PROGRESS:
          pos = mPlayer.getCurrentPosition();
          if (mPlayer.isPlaying()) {
            posCallBack(pos);
          }
          msg = Message.obtain(msg);
          mHandler.sendMessageDelayed(msg, 2000);
          break;
      }
      return false;
    }
  });

  protected void posCallBack(int position) {
  }

  public void setNeedHide(boolean needHide) {
    this.needHide = needHide;
  }

  private String stringForTime(int timeMs) {
    int totalSeconds = timeMs / 1000;

    int seconds = totalSeconds % 60;
    int minutes = (totalSeconds / 60) % 60;
    int hours = totalSeconds / 3600;

    mFormatBuilder.setLength(0);
    if (hours > 0) {
      return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
    } else {
      return mFormatter.format("%02d:%02d", minutes, seconds).toString();
    }
  }

  private int setProgress() {
    if (mPlayer == null || mDragging) {
      return 0;
    }
    int position = mPlayer.getCurrentPosition();
    int duration = mPlayer.getDuration();
    if (mProgress != null) {
      if (duration > 0) {
        // use long to avoid overflow
        long pos = 1000L * position / duration;
        mProgress.setProgress((int) pos);
      }
      int percent = mPlayer.getBufferPercentage();
      if (percent > 90) {
        //解决缓冲进度一直是90的问题，导致进度条会留有空白
        mProgress.setSecondaryProgress(100 * 10);
      } else {
        mProgress.setSecondaryProgress(percent * 10);
      }
    }

    if (mEndTime != null) mEndTime.setText(stringForTime(duration));
    if (mCurrentTime != null) mCurrentTime.setText(stringForTime(position));
    updatePausePlay();
    return position;
  }

  @Override public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        break;
      case MotionEvent.ACTION_UP:
        if (!isShowing()) {
          show(sDefaultTimeout); // show until hide is called
        } else {
          hide();
        }
        break;
      default:
        break;
    }
    return true;
  }

  @Override public boolean onTrackballEvent(MotionEvent ev) {
    show(sDefaultTimeout);
    return false;
  }

  @Override public boolean dispatchKeyEvent(KeyEvent event) {
    int keyCode = event.getKeyCode();
    final boolean uniqueDown =
        event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_DOWN;
    if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
        || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
        || keyCode == KeyEvent.KEYCODE_SPACE) {
      if (uniqueDown) {
        doPauseResume();
        show(sDefaultTimeout);
        if (mPauseButton != null) {
          mPauseButton.requestFocus();
        }
      }
      return true;
    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
      if (uniqueDown && !mPlayer.isPlaying()) {
        mPlayer.start();
        updatePausePlay();
        show(sDefaultTimeout);
      }
      return true;
    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
      if (uniqueDown && mPlayer.isPlaying()) {
        mPlayer.pause();
        updatePausePlay();
        show(sDefaultTimeout);
      }
      return true;
    } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
        || keyCode == KeyEvent.KEYCODE_VOLUME_UP
        || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
        || keyCode == KeyEvent.KEYCODE_CAMERA) {
      // don't show the controls for volume adjustment
      return super.dispatchKeyEvent(event);
    } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
      if (!needHide) {
        ((Activity) getContext()).finish();
        return true;
      }
      if (isShowing()) {
        hide();
        return true;
      }
    }
    return super.dispatchKeyEvent(event);
  }

  private final OnClickListener mPauseListener = new OnClickListener() {
    @Override public void onClick(View v) {
      doPauseResume();
      show(sDefaultTimeout);
    }
  };

  private void updatePausePlay() {
    if (mRoot == null || mPauseButton == null) return;
    if (mPlayer.isPlaying()) {
      mPauseButton.setBackgroundResource(R.drawable.selector_btn_pause);
    } else {
      mPauseButton.setBackgroundResource(R.drawable.selector_btn_play);
    }
  }

  protected void doPauseResume() {
    if (mPlayer.isPlaying()) {
      mPlayer.pause();
      if(iBasePlayEngine != null){
        iBasePlayEngine.pause();
      }
    } else {
      if(iBasePlayEngine != null){
        iBasePlayEngine.play();
      }
      mPlayer.start();
    }
    updatePausePlay();
  }


  private final OnClickListener imageBackListener = new OnClickListener() {
    @Override public void onClick(View v) {
      if (listener != null) {
        listener.finishPage();
        show(sDefaultTimeout);
      }
    }
  };

  private final OnClickListener directoryListener = new OnClickListener() {
    @Override public void onClick(View v) {
      if (listener != null) {
        listener.clickDirectory();
        show(sDefaultTimeout);
      }
    }
  };

  private final OnClickListener speedListener = new OnClickListener() {
    @Override public void onClick(View v) {
      if (listener != null) {
        listener.clickVideoSpeed();
        show(sDefaultTimeout);
      }
    }
  };

  // There are two scenarios that can trigger the seekbar listener to trigger:
  //
  // The first is the user using the touchpad to adjust the posititon of the
  // seekbar's thumb. In this case onStartTrackingTouch is called followed by
  // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
  // We're setting the field "mDragging" to true for the duration of the dragging
  // session to avoid jumps in the position in case of ongoing playback.
  //
  // The second scenario involves the user operating the scroll ball, in this
  // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
  // we will simply apply the updated position without suspending regular updates.
  private final SeekBar.OnSeekBarChangeListener mSeekListener =
      new SeekBar.OnSeekBarChangeListener() {
        @Override public void onStartTrackingTouch(SeekBar bar) {
          if (mPlayer.isPlaying()) {
            mPlayer.pause();
          }
          show(3600000);

          mDragging = true;

          // By removing these pending progress messages we make sure
          // that a) we won't update the progress while the user adjusts
          // the seekbar and b) once the user is done dragging the thumb
          // we will post one of these messages to the queue again and
          // this ensures that there will be exactly one message queued up.
          mHandler.removeMessages(SHOW_PROGRESS);
        }

        @Override public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
          if (!fromuser) {
            // We're not interested in programmatically generated changes to
            // the progress bar's position.
            return;
          }
          long duration = mPlayer.getDuration();
          long newposition = (duration * progress) / 1000L;
          if (mCurrentTime != null) mCurrentTime.setText(stringForTime((int) newposition));
        }

        @Override public void onStopTrackingTouch(SeekBar bar) {
          long duration = mPlayer.getDuration();
          long newposition = (duration * bar.getProgress()) / 1000L;
          mPlayer.seekTo((int) newposition);
          mPlayer.start();
          mDragging = false;
          setProgress();
          updatePausePlay();
          show(sDefaultTimeout);

          if(iBasePlayEngine != null){
            iBasePlayEngine.skipTo((int) newposition);
          }

          // Ensure that progress is properly updated in the future,
          // the call to show() does not guarantee this because it is a
          // no-op if we are already showing.
          mHandler.sendEmptyMessage(SHOW_PROGRESS);
        }
      };

  @Override public void setEnabled(boolean enabled) {
    if (mPauseButton != null) {
      mPauseButton.setEnabled(enabled);
    }
    if (mProgress != null) {
      mProgress.setEnabled(enabled);
    }
    disableUnsupportedButtons();
    super.setEnabled(enabled);
  }

  public void setDefaultTimeout(int sDefaultTimeout) {
    this.sDefaultTimeout = sDefaultTimeout;
  }

  LessonRecordVideoListener listener;

  public interface LessonRecordVideoListener {
    void finishPage();
    void clickDirectory();
    void clickVideoSpeed();
    void screenShot();
  }

  public void setListener(String mediaName, LessonRecordVideoListener listener) {
    this.mediaName = mediaName;
    this.listener = listener;
  }

}
